#pragma once;
#include "Object.h"

// Event template class for encapsulating event callback functions.
template< class ArgumentType >
class Delegate
{
public:
    typedef boost::function< void ( ArgumentType& ) > FunctionType;
    //typedef boost::signal< void ( typename ArgumentType& ) > FunctionType;
    typedef boost::signals::connection ConnectionType;

    // Add a callback to the the list
    // Returns the connection object that can be used to disconnect the 
    // subscriber from the signal.
    ConnectionType operator += ( typename const FunctionType& callback ) const
    {
        return m_Callbacks.connect( callback );
    }

    // Remove a callback from the list
    void operator -= ( typename const FunctionType& callback ) const
    {
        assert(false);
        // TODO: This isn't working yet.. Getting a compiler error:
        // Error	1	error C2666: 'boost::operator ==' : 4 overloads have similar conversions signal_template.hpp
        // WORKAROUND: Use the connection object returned when the subscriber was initially connected
        // to disconnect the subscriber.
        m_Callbacks.disconnect( callback );
    }

    void operator -= ( ConnectionType& con )
    {
        m_Callbacks.disconnect( con ); // This doesn't seem to work either!?
        if ( con.connected() )
        {
            con.disconnect(); // This is stupid, then all connections disconnected in this way even if it was never registered with the signal!
        }
    }

    // Invoke this event with the argument
    void operator()( typename ArgumentType& argument )
    {
        m_Callbacks( argument );
    }

private:
    typedef boost::signal< void (ArgumentType&) > Callbacks;
    mutable Callbacks   m_Callbacks;
};

// Base class for all event args
class EventArgs
{
public:
    EventArgs( const Object& caller )
        : Caller(caller)
    {}

    // The object that invoked the event
    const Object& Caller;
};
// Standard event type
typedef Delegate<EventArgs> Event;

class KeyEventArgs : public EventArgs
{
public:
    enum KeyState
    {
        KS_Released = 0,
        KS_Pressed = 1
    };

    typedef EventArgs base;
    KeyEventArgs( const Object& caller, SDLKey key, SDLMod mod, Uint8 state, Uint16 unicode )
        : base( caller )
        , Key(key)
        , Modifier(mod)
        , State((KeyState)state)
        , Unicode(unicode)
    {}

    SDLKey      Key;
    SDLMod      Modifier;
    KeyState    State;
    Uint16      Unicode;
};
typedef Delegate<KeyEventArgs> KeyboardEvent;

class MouseMotionEventArgs : public EventArgs
{
public:
    typedef EventArgs base;
    MouseMotionEventArgs( const Object& caller, Uint8 buttonState, Uint16 absX, Uint16 absY, Sint16 relX, Sint16 relY )
        : base( caller )
        , ButtonState( buttonState )
        , AbsPosition( absX, absY )
        , RelPosition( relX, relY )
    {}

    // The current state of the mouse buttons
    Uint8      ButtonState;

    // The absolute position of the mouse in screen coordinates?
    glm::ivec2 AbsPosition;
    // The relative position of the mouse (delta since last measurement).
    glm::ivec2 RelPosition;

};
typedef Delegate<MouseMotionEventArgs> MouseMotionEvent;

class MouseButtonEventArgs : public EventArgs
{
public:
    enum ButtonState
    {
        BS_Released = 0,
        BS_Pressed = 1
    };

    typedef EventArgs base;
    MouseButtonEventArgs( const Object& caller, Uint8 buttonID, Uint8 state, Uint16 absX, Uint16 absY )
        : base( caller )
        , ButtonID( buttonID )
        , State( (ButtonState)state )
        , AbsPosition( absX, absY )
    {}

    // The ID of the button the triggered the event
    Uint8       ButtonID;
    // The state (Released, Pressed)
    ButtonState State;
    // The absolute position of the mouse when the event was triggered
    glm::ivec2  AbsPosition;
};
typedef Delegate<MouseButtonEventArgs> MouseButtonEvent;

class ResizeEventArgs : public EventArgs
{
public:
    typedef EventArgs base;
    ResizeEventArgs( const Object& caller, int width, int height )
        : base( caller )
        , Dimensions( width, height )
    {}

    // The new dimensions of the window
    glm::ivec2 Dimensions;

};
typedef Delegate<ResizeEventArgs> ResizeEvent;

class UpdateEventArgs : public EventArgs
{
public:
    typedef EventArgs base;
    UpdateEventArgs( const Object& caller, float fDeltaTime )
        : base( caller )
        , ElapsedTime( fDeltaTime )
    {}

    float ElapsedTime;
};
typedef Delegate<UpdateEventArgs> UpdateEvent;

class RenderEventArgs : public EventArgs
{
public:
    typedef EventArgs base;
    RenderEventArgs( const Object& caller, float fDeltaTime )
        : base( caller )
        , ElapsedTime( fDeltaTime )
    {}

    float ElapsedTime;
};
typedef Delegate<RenderEventArgs> RenderEvent;

class UserEventArgs : public EventArgs
{
public:
    typedef EventArgs base;
    UserEventArgs( const Object& caller, int code, void* data1, void* data2 )
        : base( caller )
        , Code( code )
        , Data1( data1 )
        , Data2( data2 )
    {}

    int     Code;
    void*   Data1;
    void*   Data2;
};
typedef Delegate<UserEventArgs> UserEvent;

class RuntimeErrorEventArgs : public EventArgs
{
public:
    typedef EventArgs base;

    RuntimeErrorEventArgs( const Object& caller, CGerror error, const std::string& errorString, const std::string& compilerError )
        : base ( caller )
        , ErrorCode( error )
        , ErrorString( errorString )
        , CompilerError( compilerError )
    {}

    CGerror ErrorCode;
    std::string ErrorString;
    std::string CompilerError;    
};
typedef Delegate<RuntimeErrorEventArgs> RuntimeErrorEvent;

class Effect;

class EffectLoadedEventArgs : public EventArgs
{
public:
    typedef EventArgs base;

    EffectLoadedEventArgs( const Object& caller, Effect& effect )
        : base( caller )
        , Effect( effect )
    {}

    // The effect that was loaded.
    // (non-const so we can do something like apply uniform parameters)
    Effect& Effect;
};
typedef Delegate<EffectLoadedEventArgs> EffectLoadedEvent;
